﻿#ifndef PILESTOCKAGE_H
#define PILESTOCKAGE_H
/**
 \file pilereels.h
 \brief Pile des réels
 \author Baptiste Pirault et  Hugo Dubois
 \date 01/06/2012
 \version 1
 **/

#include <QStack>
#include <QString>
#include <iostream>
#include <QSettings>

#include "exception.h"
#include "pileaffichage.h"
#include "fonctions.h"
#include "etat.h"

/*!
 \class pileStockage<T>
 \brief classe template abstraite permettant d'unifer l'interface avec les piles
  */

template <class T>
class pileStockage : public QStack<T>
{
public:
    //! \brief Constructeur
    //!
    //! Initialise la constante MAX, affecte aux attribut PileAffichage et QTextEdit les valeurs de *p et *affichagePile.
    //! Initialise la pile QStack (par héritage)
    //! \param *p : Pointeur sur la pile d'affichage
    //! \param *affichagePile : Pointeur sur QTextEdit. Affichage de l'interface
    pileStockage(PileAffichage *p,QTextEdit *affichagePile);

    //! \brief addX méthode virtuel pure
    //! \param c : QString contenant le caractère (chiffre à rajouter)
    virtual void addX(const QString c) = 0;

    //! \brief iniX méthode virtuel pure
    //! Réinitialise les valeurs
    virtual void iniX() = 0;

    //! \brief Accesseur virtuel pure de X de type T
    //! \return X de type T
    virtual T getX()const = 0;

    //! \brief Retourne X sous forme de QString (méthode virtuel)
    //! \return Retourne un QString contenant X. Mis en forme pour l'affichage
    virtual QString getQStringX()const = 0;

    //! \brief Met à jour l'afficheur de la pile (méthode virtuel)
    //!
    //! Affiche dans l'interface le contenu de la pile
    virtual void updateAfficheur()const = 0;

    //! \brief Retire le dernier caractère ajouté à X (méthode virtuel)
    virtual void back() = 0;

    //! \brief Sauvegarde la pile dans le fichier mesOptions.ini (méthode virtuel)
    virtual void sauvegarder()const = 0;

    //! \brief Redefinition du clear : nettoyage de la pile, valeur par défaut des attributs et nettoyage de l'affichage
    //!
    //! Appel de iniX, clear et updateAfficheur pour réinitialiser l'affichage
    void clear();

    //! \brief Redéfinition de pop
    //!
    //! Retire dans la pile (pop) puis appel de updateAfficheur().
    //! \return Retourne un élément de type T
    T pop();

    //! \brief Redefinition de push
    //!
    //! Ajoute un élément T dans la pile (push) puis appel de updateAfficheur().
    //! \param &t : Référence l'élément T à mettre dans la pile
    void push(const T & t);

    //! \brief Echange deux éléments d'indice a,b de la pile
    //!
    //! \param a : indice réel
    //! \param b :  indice réel
    void swap(const double a,const double b);

    //! \brief Somme les a premiers éléments de la pile
    //!
    //! \param a : nombre de chiffres à sommer
    void sum(const double a);

    //! \brief Fait la moyenne des a premier élément de la pile
    //!
    //! \param a : nombre de chiffre sur lesquels il faut faire la moyenne
    void mean(const double a);

    //! \brief sauvegarde un état passé en argument dans la pile pundo
    //!
    //! \param e : état à sauvegarder
    void save(etat<T> e);

    //! \brief surcharge de save(etat<T> e)
    void save(int x, QString s, T x1 = T(), T x2 = T());

    //! \brief Annule la dernière action
    void undo();

    //! \brief Rétablie une annulation
    void redo();

protected:
    //! \brief Valeur Maximale du nombre de type T
    //!	\def MAX = 1000000000000000
    double MAX;

    //! \brief Pointeur de type PileAffichage
    //!
    //! La pile d'affichage permet de stocker les résultats à afficher
    PileAffichage* pA; // Affichage des résultats

    //! \brief Pointeur sur un QtextEdit
    //!
    //! Zone de texte placée sur l'interface
    QTextEdit *aPile; // Afficheur pour pile

    //! \brief Réel en construction
    //!
    //! Le réel en construction dans l'interface est stocké dans cette variable en attendant d'être mis dans la pile
    T x; // Variable pour stocker le nombre en construction

    //! \brief Pile de stockage des états sauvegarder par la fonction save
    QStack<etat<T>> pundo;

    //! \brief Pile de stockage des états annuler qui pourront être utilisé par la fonction redo()
    QStack<etat<T>> predo;
};


template <class T>
pileStockage<T>::pileStockage(PileAffichage* p, QTextEdit *affichagePile) :
    QStack(),pA(p),aPile(affichagePile)
{
    std::cout<<"Nouvelle pile crée" << std::endl;
    save(-1,"");
    MAX = 1000000000000000;
}

template <class T>
void pileStockage<T>::clear(){
    iniX();
    pA->clear();
    pundo.clear();
    predo.clear();
    save(-1,"");
    QStack<T>::clear();
    updateAfficheur();
}

template <class T>
T pileStockage<T>::pop(){
    T x = QStack::pop();
    updateAfficheur();
    return x;
}

template <class T>
void pileStockage<T>::push(const T & t){
    QStack::push(t);
    updateAfficheur();
}

template <class T>
void pileStockage<T>::swap(const double a,const double b){
    if (size() < a || size() < b) return;
    T tmp = value(size()-a);
    replace(size()-a,value(size()-b));
    replace(size()-b,tmp);
    pundo.clear();
    predo.clear();
    updateAfficheur();
}

template <class T>
void pileStockage<T>::sum(const double a){
    if (size() < a) return;
    T somme = 0;
    for (int i = 1; i <= a; i++)
        somme += pop();
    push(somme); // l'updateAfficheur est dedans
    pundo.clear();
    predo.clear();
}

template <class T>
void pileStockage<T>::mean(const double a){
    if (size() < a) return;
    T moyenne = 0;
    for (int i = 1; i <= a; i++)
        moyenne += pop();
    moyenne = moyenne/a;
    push(moyenne); // l'updateAfficheur est dedans
    pundo.clear();
    predo.clear();
}

/* --- Gestion de undo/redo --- */


template <class T>
void pileStockage<T>::save(etat<T> e){
    pundo.push(e);
    predo.clear();
}

template <class T>
void pileStockage<T>::save(int x, QString s, T x1, T x2){
    save(etat<T>(x, s, x1, x2));
}

template <class T>
void pileStockage<T>::undo(){
    std::cout<<"Annulation"<<std::endl;
    etat<T> avant,actuel;
    T tmp;
    if(pundo.size() > 1){
        actuel = pundo.pop(); // Récupération de l'état actuel a passer à predo
        avant = pundo.last(); // Etat que l'on doit restituer, que l'on ne dépile pas !
        // On revient a l'état précédant
        switch(actuel.action){
        case 0: // espace
            QStack::pop(); // On retire de la pile de stockage le dernier élément empilé
            break;
        case 1: // unaire
            actuel.n2 = QStack::pop(); // On récupère le résultat de l'opération uniaire pour pouvoir le rétablir dans redo
            QStack::push(actuel.n1); // on rempile l'opérande
            break;
        case 2: // binaire
            tmp = QStack::pop(); // On récupère le résultat de l'opération uniaire
            QStack::push(actuel.n1); // on rempile les opérandes
            QStack::push(actuel.n2);
            actuel.n1 = tmp; // on enregistre le résultat pour pouvoir le rétablir dans redo, n1 est écraser mais sera récupéré dans redo dans le deuxième pop
            break;
        case 3: // =
            pA->pop();pA->pop(); // On retire les deux dernière ligne de l'afficheur : l'opération et le =X
            break;
        default:
            return;
            break;
        }
        pA->iniText(); // On réinitialise l'affichage de la ligne en cours
        if (avant.action != 3)
            pA->addText(avant.texte); // On affiche le texte de l'état avant que l'on restitue
        else
            pA->addText("");
        updateAfficheur(); // la pile de stockage est modifier donc il faut actualiser l'afficheur de la pile
        // et on ajoute dans predo
        predo.push(actuel); // l'état que l'on vient de annuler peut être à rétablie en le mettant en pile dans predo
    }
}

template <class T>
void pileStockage<T>::redo(){
    std::cout<<"Retablir"<<std::endl;
    etat<T> futur;
    T tmp;
    if(!predo.isEmpty()){ // s'il reste des états à rétablir
        futur = predo.pop(); // Etat que l'on doit restituer (c'est le dernier qui a été empiler dans undo())
        // On va à l'état futur
        switch(futur.action){
        case 0: // espace
            // si l'action espace à été annuler, cela veut dire que l'on a retirer n1 de la pile de stockage donc on le remet en pile
            QStack::push(futur.n1);
            break;
        case 1: // unaire
            // si une action unaire à été annuler,
                // il faut dépiler l'opérande et empiler le résultat qui a été stocker par undo dans n2
            QStack::pop();
            QStack::push(futur.n2);
            break;
        case 2: // binaire
            // si une action binaire à été annuler,
                // il faut dépiler les deux opérandes et empiler le résultat qui a été stocker par undo dans n1
            QStack::pop();
            tmp = QStack::pop();
            QStack::push(futur.n1);
            futur.n1 = tmp; // récupération de l'opréande n1 qui avait été écrasé pour sauvegarder le résultat
            break;
        case 3: // =
            // si l'action = à été annuler,
                // il n'y a pas de changement en pile de stockage mais en pile d'affichage,
                // il faut mettre le texte en pile et =X
            pA->push(futur.texte);
            pA->push("= "+toQString(futur.n1));
            break;
        default:
            return;
            break;
        }
        pA->iniText(); // On réinitialise l'affichage de la ligne en cours
        if (futur.action != 3)
            pA->addText(futur.texte); // On met dans la ligne courrante le texte adéquate
        else
            pA->addText("");
        updateAfficheur(); // la pile de stockage est modifier donc il faut actualiser l'afficheur de la pile
        pundo.push(futur); // l'état que l'on vient de rétablir peut être à nouveau annuler en le mettant en pile dans pundo
    }
}


#endif // PILESTOCKAGE_H
